package com.admin.system.modules.system.service.impl;

import com.admin.common.constant.ExceptionConstant;
import com.admin.common.enums.CodeEnum;
import com.admin.common.exception.BadRequestException;
import com.admin.common.exception.EntityExistException;
import com.admin.common.utils.*;
import com.admin.component.web.MultipartFileUtil;
import com.admin.core.dto.UserPasswordDTO;
import com.admin.core.dto.UserQueryDTO;
import com.admin.core.dto.cache.ParamConfigCacheBO;
import com.admin.core.entity.SysRole;
import com.admin.core.entity.SysUser;
import com.admin.core.entity.SysUserJob;
import com.admin.core.entity.SysUserRole;
import com.admin.core.mapper.RoleMapper;
import com.admin.core.mapper.UserJobMapper;
import com.admin.core.mapper.UserMapper;
import com.admin.core.mapper.UserRoleMapper;
import com.admin.oauth.component.UserCacheManager;
import com.admin.oauth.util.DataScopeUtil;
import com.admin.oauth.util.LoginUserUtil;
import com.admin.system.constant.SystemConstant;
import com.admin.system.modules.system.service.UserService;
import com.admin.system.modules.system.service.VerifyService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author sq-lan
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, SysUser> implements UserService {

    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final UserJobMapper userJobMapper;
    private final UserRoleMapper userRoleMapper;
    private final UserCacheManager userCacheManager;
    private final PasswordEncoder passwordEncoder;
    private final VerifyService verificationCodeService;

    @Override
    public PageResult<SysUser> paging(UserQueryDTO dto) {
        PageHelper.startPage(dto.getCurrent(), dto.getSize());
        List<SysUser> list = list(dto);
        return PageUtil.toPageHelper(list);
    }

    @Override
    public List<SysUser> list(UserQueryDTO dto) {
        dto.setDataScopeSql(DataScopeUtil.dataScope());
        List<SysUser> list = userMapper.list(dto);
        if (list.isEmpty()) {
            return list;
        }
        // 加载用户配置的角色
        List<Integer> ids = list.stream().map(SysUser::getId).collect(Collectors.toList());
        List<SysUserRole> userRoles = userRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getUserId, ids));
        Map<Integer, List<SysUserRole>> userRoleMap = userRoles.stream().collect(Collectors.groupingBy(SysUserRole::getUserId));
        List<Integer> roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
        List<SysRole> roles = roleMapper.selectList(Wrappers.<SysRole>lambdaQuery().in(SysRole::getId, roleIds));
        Map<Integer, SysRole> roleMap = roles.stream().collect(Collectors.toMap(SysRole::getId, a -> a, (k1, k2) -> k1));
        Map<Integer, List<SysRole>> userRoleMap2 = new HashMap<>(16);
        for (Map.Entry<Integer, List<SysUserRole>> entry : userRoleMap.entrySet()) {
            List<SysRole> temps = new ArrayList<>(entry.getValue().size());
            for (SysUserRole userRole : entry.getValue()) {
                temps.add(roleMap.get(userRole.getRoleId()));
            }
            userRoleMap2.put(entry.getKey(), temps);
        }
        for (SysUser user : list) {
            user.setRoles(userRoleMap2.get(user.getId()));
        }
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysUser entity) {
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, entity.getUsername())) > 0) {
            throw new EntityExistException("用户名", entity.getUsername());
        }
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getEmail, entity.getEmail())) > 0) {
            throw new EntityExistException("邮箱", entity.getEmail());
        }
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getPhone, entity.getPhone())) > 0) {
            throw new EntityExistException("电话", entity.getPhone());
        }
        this.save(entity);
        // 保存用户岗位
        if (entity.getJobs() != null && !entity.getJobs().isEmpty()) {
            userJobMapper.insertData(entity.getId(), entity.getJobs());
        }
        // 保存用户角色
        if (entity.getRoles() != null && !entity.getRoles().isEmpty()) {
            userRoleMapper.insertData(entity.getId(), entity.getRoles());
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modify(SysUser entity) {
        DataScopeUtil.overstep(entity.getDeptId(), entity.getCreateBy());
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, entity.getUsername()).ne(SysUser::getId, entity.getId())) > 0) {
            throw new EntityExistException("用户名", entity.getUsername());
        }
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getEmail, entity.getEmail()).ne(SysUser::getId, entity.getId())) > 0) {
            throw new EntityExistException("邮箱", entity.getEmail());
        }
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getPhone, entity.getPhone()).ne(SysUser::getId, entity.getId())) > 0) {
            throw new EntityExistException("电话", entity.getPhone());
        }
        // 如果用户被禁用，则清除用户登录信息
        if (!entity.getEnabled()) {
            userCacheManager.kickOut(entity.getUsername());
        }
        SysUser user = new SysUser();
        user.setId(entity.getId());
        user.setDeptId(entity.getDeptId());
        user.setUsername(entity.getUsername());
        user.setEmail(entity.getEmail());
        user.setEnabled(entity.getEnabled());
        user.setPhone(entity.getPhone());
        user.setNickName(entity.getNickName());
        user.setGender(entity.getGender());
        this.updateById(user);
        // 更新用户岗位
        userJobMapper.delete(Wrappers.<SysUserJob>lambdaQuery().eq(SysUserJob::getUserId, entity.getId()));
        userJobMapper.insertData(entity.getId(), entity.getJobs());
        // 更新用户角色
        userRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, entity.getId()));
        userRoleMapper.insertData(entity.getId(), entity.getRoles());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Set<Integer> ids) {
        List<SysUser> list = userMapper.selectBatchIds(ids);
        for (SysUser user : list) {
            DataScopeUtil.overstep(user.getDeptId(), user.getCreateBy());
        }
        userMapper.deleteBatchIds(ids);
        // 删除用户岗位
        userJobMapper.delete(Wrappers.<SysUserJob>lambdaQuery().in(SysUserJob::getUserId, ids));
        // 删除用户角色
        userRoleMapper.delete(Wrappers.<SysUserRole>lambdaQuery().in(SysUserRole::getUserId, ids));
    }

    @Override
    public void modifyMy(SysUser entity) {
        if (count(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getPhone, entity.getPhone()).ne(SysUser::getId, entity.getId())) > 0) {
            throw new EntityExistException("电话", entity.getPhone());
        }
        SysUser user = new SysUser();
        user.setId(entity.getId());
        user.setNickName(entity.getNickName());
        user.setPhone(entity.getPhone());
        user.setGender(entity.getGender());
        this.updateById(user);
    }

    @Override
    public void updatePassword(UserPasswordDTO passVo) throws Exception {
        String oldPass = RsaUtils.decryptByPrivateKey(RsaUtils.RSA_PRIVATE_KEY, passVo.getOldPass());
        String newPass = RsaUtils.decryptByPrivateKey(RsaUtils.RSA_PRIVATE_KEY, passVo.getNewPass());
        SysUser user = this.getById(LoginUserUtil.userId());
        if (!passwordEncoder.matches(oldPass, user.getPassword())) {
            throw new BadRequestException(ExceptionConstant.OLD_PASSWORD_ERROR);
        }
        if (passwordEncoder.matches(newPass, user.getPassword())) {
            throw new BadRequestException(ExceptionConstant.NEW_PASSWORD_EQ_OLD_PASSWORD);
        }
        SysUser updateObj = new SysUser();
        updateObj.setPassword(passwordEncoder.encode(newPass));
        updateObj.setId(user.getId());
        this.updateById(updateObj);
    }

    @Override
    public void updateEmail(String code, SysUser entity) throws Exception {
        String password = RsaUtils.decryptByPrivateKey(RsaUtils.RSA_PRIVATE_KEY, entity.getPassword());
        SysUser user = this.getById(LoginUserUtil.userId());
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadRequestException(ExceptionConstant.PASSWORD_ERROR);
        }
        verificationCodeService.validated(CodeEnum.EMAIL_RESET_EMAIL_CODE.getKey() + user.getEmail(), code);
        SysUser updateObj = new SysUser();
        updateObj.setEmail(entity.getEmail());
        updateObj.setId(user.getId());
        this.updateById(updateObj);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> updateAvatar(MultipartFile multipartFile) {
        // 文件大小验证
        FileUtil.checkSize(ParamConfigCacheBO.fileParamConfig.getAvatarMaxSize(), multipartFile.getSize());
        String fileType = FileUtil.getExtensionName(multipartFile.getOriginalFilename());
        if (fileType != null && !FileUtil.IMAGE_TYPE.contains(fileType)) {
            throw new BadRequestException("文件格式错误！, 仅支持 " + FileUtil.IMAGE_TYPE + " 格式");
        }
        SysUser user = LoginUserUtil.user();
        String oldPath = user.getAvatarPath();
        File file = MultipartFileUtil.upload(multipartFile, SystemConstant.avatarPath());
        user.setAvatarPath(Objects.requireNonNull(file).getPath());
        user.setAvatarName(file.getName());
        this.updateById(user);
        if (StringUtils.isNotBlank(oldPath)) {
            FileUtil.del(oldPath);
        }
        return new HashMap<String, String>(1) {{
            put("avatar", file.getName());
        }};
    }


}
